package com.itheima.train.manager.order.service;

import com.alibaba.fastjson.JSON;
import com.itheima.train.manager.domain.*;
import com.itheima.train.manager.order.fegin.OrderFeignClient;
import com.itheima.train.manager.order.mq.RabbitmqProducer;
import com.itheima.train.manager.utils.SnowFlakeGenerator;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

@Service
public class OrderService {
    
    // 日志记录器对象
    private Logger LOGGER = LoggerFactory.getLogger(OrderService.class);
    
    // 定义前端参数和座位性质之间的Map集合
    private static Map<String, String> seatNatureHashMap = new HashMap<String, String>();
    
    static {
        seatNatureHashMap.put("busSeat", "2");
        seatNatureHashMap.put("firstSeat", "3");
        seatNatureHashMap.put("secondSeat", "4");
        seatNatureHashMap.put("hardSleeper", "7");
        seatNatureHashMap.put("softSleeper", "6");
        seatNatureHashMap.put("hardSeat", "5");
        seatNatureHashMap.put("softSeat", "9");
    }
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // 创建一个雪花算法对应的类对象
    private SnowFlakeGenerator snowFlakeGenerator = new SnowFlakeGenerator(0, 2);
    
    // 创建一个线程池
    private ExecutorService executorService = Executors.newFixedThreadPool(10);
    
    @Autowired
    private RabbitmqProducer rabbitmqProducer;
    
    @Autowired
    private OrderFeignClient orderFeignClient;
    
    @Autowired
    private InterProcessMutex lock;
    
    // 生成订单
    
    /**
     * 测试nginx限流使用
     *
     * @param orderParams
     * @return
     */
    public ResponseResult<String> submitOrder1(OrderParams orderParams) {
        return new ResponseResult<>();
    }
    
    /**
     * 下单操作
     *
     * @param orderParams
     * @return
     */
    public ResponseResult<String> submitOrder(OrderParams orderParams) {
        
        // 1. 创建ResponseResult对象
        ResponseResult<String> responseResult = new ResponseResult<>();
        
        // 从orderParams中获取一些请求参数
        String trainNum = orderParams.getTrainNum();
        String trainRidingDate = orderParams.getTrainRidingDate();
        Long ridingPlanId = orderParams.getRidingPlanId();
        String ticketType = orderParams.getTicketType();            // 座位类型的取值：busseat   busSeatStock
        String redisTicketTypeKey = ticketType + "Stock";
        
        String ridingPlanDateKey = "riding_plan:" + trainNum + ":" + trainRidingDate + ":" + ridingPlanId;
        
        // 进行订单详情信息的查询
        Order dbOrder = orderFeignClient.queryOrderInfo(orderParams.getUserId(), trainNum, trainRidingDate);
        if (dbOrder != null) {
            responseResult.setResult(false);
            responseResult.setMessage("1");
            responseResult.setData(String.valueOf(dbOrder.getId()));
            return responseResult;
        }
        
        // 获取指定日期的乘车计划数据，并将其封装到一个TbRidingPlanDate对象中
        Map<Object, Object> ridingPlanDateHashMap = redisTemplate.boundHashOps(ridingPlanDateKey).entries();
        TbRidingPlanDate tbRidingPlanDate = JSON.parseObject(JSON.toJSONString(ridingPlanDateHashMap), TbRidingPlanDate.class);
        
        String seatNature = seatNatureHashMap.get(ticketType);
        //      2、遍历集合获取每一个车厢数据（集合）
        String coach = null;
        String seatNo = null;
        
        // 2. 判断指定日期的乘车计划数据所对应的座位信息是否存在库存
        //  riding_plan:G26:2020-06-01:270101019289976832
        try {
            
            lock.acquire(5, TimeUnit.MINUTES);
            
            LOGGER.info("【分布式锁获取成功了】 ---> " + JSON.toJSONString(orderParams));
            
            Thread.sleep(20000);            // 模拟业务的处理时长
            
            LOGGER.info("【预扣库存开始了】 ---> " + JSON.toJSONString(orderParams));
            int count = Integer.parseInt(redisTemplate.boundHashOps(ridingPlanDateKey).get(redisTicketTypeKey).toString());
            if (count <= 0) {
                responseResult.setResult(false);
                responseResult.setMessage("0");
                return responseResult;
            }
            
            // 3. 动态库存的扣减： 完成我们这个列车车次所有的乘车计划所对应的座位数的库存扣减
            String stockCountKeyDynamic = "riding_plan:" + trainNum + ":" + trainRidingDate + ":*";
            Set<String> keys = redisTemplate.keys(stockCountKeyDynamic);
            for (String key : keys) {
                redisTemplate.boundHashOps(key).put(redisTicketTypeKey, String.valueOf(count - 1));
            }
            LOGGER.info("【预扣库存结束了】 ---> " + JSON.toJSONString(orderParams));

//        1、获取所有的指定座位类型的所有车厢的key（集合）
            LOGGER.info("【分配座位开始了】 ---> " + JSON.toJSONString(orderParams));
            // 分配座位: train_seat:G26:2020-06-01:4:6
            String seatNatureKey = "train_seat:" + trainNum + ":" + trainRidingDate + ":" + seatNature + ":*";
            Set<String> allSeatNatureCoach = redisTemplate.keys(seatNatureKey);
            
            out:
            for (String key : allSeatNatureCoach) {
                Map<Object, Object> entries = redisTemplate.boundHashOps(key).entries();
                coach = entries.get("coach_num").toString();
                
                // 3、遍历集合获取每一个座位所对应的状态值
                for (Object seatNoKey : entries.keySet()) {
                    
                    String seatStatus = entries.get(seatNoKey).toString();
                    
                    // 4、状态值如果是0，记录座位标号
                    if ("0".equals(seatStatus)) {
                        seatNo = seatNoKey.toString();
                        
                        // 5、更改座位状态值
                        redisTemplate.boundHashOps(key).put(seatNoKey.toString(), "1");
                        break out;
                    }
                    
                }
                
            }
            LOGGER.info("【分配座位结束了】 ---> coach ---> {} , seatNo ---> {} ", coach, seatNo);
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                lock.release();
                LOGGER.info("【分布式锁释放了】 ---> " + JSON.toJSONString(orderParams));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        String finalCoachNum = coach;
        String finalSeatNum = seatNo;
        Callable<ResponseResult<String>> callable = () -> {
            
            // 生成id
            LOGGER.info("【构建订单开始了】 ---> " + JSON.toJSONString(orderParams));
            long orderId = snowFlakeGenerator.nextId();
            
            // 构建订单数据
            Order order = new Order();
            order.setId(orderId);
            order.setCoachNum(finalCoachNum);
            order.setIsEffect("1");
            order.setPayStatus("0");
            order.setRidingPlanDateId(orderParams.getRidingPlanId());
            order.setSeatNature(seatNature);
            order.setSeatNum(finalSeatNum);
            order.setTrainNum(orderParams.getTrainNum());
            order.setUserId(orderParams.getUserId());
            order.setTrainRidingDate(orderParams.getTrainRidingDate());
            order.setStartStationName(tbRidingPlanDate.getStartStationName());
            order.setReceiveStationName(tbRidingPlanDate.getReceiveStationName());
            
            if ("终点站".equals(tbRidingPlanDate.getStartTime())) {
                order.setStartTime(tbRidingPlanDate.getReceiveStartStationTime());
            } else {
                order.setStartTime(tbRidingPlanDate.getStartTime());
            }
            
            order.setEndTime(tbRidingPlanDate.getReceiveEndStationTime());
            
            // 根据座位情况设置具体的票价
            if ("7".equals(seatNature)) {   // 硬卧
                if (finalSeatNum.contains("上铺")) {
                    order.setSeatPrice(tbRidingPlanDate.getHardSleeperUpPrice());
                } else if (finalSeatNum.contains("中铺")) {
                    order.setSeatPrice(tbRidingPlanDate.getHardSleeperMiddlePrice());
                } else if (finalSeatNum.contains("下铺")) {
                    order.setSeatPrice(tbRidingPlanDate.getHardSleeperDownPrice());
                }
            } else if ("6".equals(seatNature)) {      // 软卧
                if (finalSeatNum.contains("上铺")) {
                    order.setSeatPrice(tbRidingPlanDate.getSoftSleeperUpPrice());
                } else if (finalSeatNum.contains("下铺")) {
                    order.setSeatPrice(tbRidingPlanDate.getSoftSleeperDownPrice());
                }
            } else if ("3".equals(seatNature)) {
                order.setSeatPrice(tbRidingPlanDate.getFirstSeatPrice());
            } else if ("4".equals(seatNature)) {
                order.setSeatPrice(tbRidingPlanDate.getSecondSeatPrice());
            } else if ("2".equals(seatNature)) {
                order.setSeatPrice(tbRidingPlanDate.getBusSeatPrice());
            } else if ("5".equals(seatNature)) {
                order.setSeatPrice(tbRidingPlanDate.getHardSeatPrice());
            } else if ("9".equals(seatNature)) {
                order.setSeatPrice(tbRidingPlanDate.getSoftSeatPrice());
            }
            LOGGER.info("【构建订单结束了】 ---> " + JSON.toJSONString(order));
            
            LOGGER.info("【Redis排队开始了】 ---> " + JSON.toJSONString(order));
            String sortedKey = trainNum + ":" + trainRidingDate + ":" + orderParams.getUserId();
            redisTemplate.boundZSetOps("train_manager:sorted_queue").add(sortedKey, System.nanoTime());
            LOGGER.info("【Redis排队结束了】 ---> " + JSON.toJSONString(order));
            
            // 创建OrderSynES对象
            LOGGER.info("【发送ES同步库存数据开始了】 ---> " + JSON.toJSONString(order));
            OrderSynEs orderSynEs = new OrderSynEs();
            orderSynEs.setRidingPlanDate(trainRidingDate);
            orderSynEs.setSeatNature(seatNature);
            orderSynEs.setTrainNum(trainNum);
            rabbitmqProducer.sendMessage(JSON.toJSONString(orderSynEs), "syn_stock");
            LOGGER.info("【发送ES同步库存数据结束了】 ---> " + JSON.toJSONString(orderSynEs));
            
            // 创建一个OrderHandler对象
            LOGGER.info("【发送订单数据开始了】 ---> " + JSON.toJSONString(order));
            User user = JSON.parseObject(redisTemplate.boundHashOps("train_manager:userInfo").get(orderParams.getUserId()).toString(), User.class);
            OrderHandler orderHandler = new OrderHandler();
            orderHandler.setOrder(order);
            orderHandler.setUser(user);
            rabbitmqProducer.sendMessage(JSON.toJSONString(orderHandler), "gen_order");
            LOGGER.info("【发送订单数据开始了】 ---> " + JSON.toJSONString(orderHandler));
            
            // 发送延迟消息到MQ中
            LOGGER.info("【发送延迟消息开始了】 ---> " + JSON.toJSONString(order));
            HashMap<String, Long> msg = new HashMap<>();
            msg.put("orderId", order.getId());
            rabbitmqProducer.sendMessage(JSON.toJSONString(msg), "order_timeout");
            LOGGER.info("【发送延迟消息结束了】 ---> " + JSON.toJSONString(order));
            
            responseResult.setResult(true);
            return responseResult;
            
        };
        
        try {
            // 执行任务类
            Future<ResponseResult<String>> future = executorService.submit(callable);
            ResponseResult<String> stringResponseResult = future.get();
            return stringResponseResult;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        
        return new ResponseResult<>();
    }
    
    // 查询排队信息
    public ResponseResult<String> querySortedInfo(OrderParams orderParams) {
        
        // 创建ResponseResult对象
        ResponseResult<String> responseResult = new ResponseResult<>();
        
        // 从Redis中去获取排队信息
        String sortedKey = orderParams.getTrainNum() + ":" + orderParams.getTrainRidingDate() + ":" + orderParams.getUserId();
        System.out.println(sortedKey);
        Long rank = redisTemplate.boundZSetOps("train_manager:sorted_queue").rank(sortedKey);
        System.out.println(rank);
        
        // 判断结果
        if (rank == null) {
            responseResult.setResult(false);
        } else {
            responseResult.setResult(true);
            responseResult.setData(String.valueOf(rank));
        }
        
        // 返回
        return responseResult;
        
    }
    
    // 根据订单的id去获取订单的详情信息
    public OrderHandler queryById(Long orderId) {
        
        // 远程调用获取订单数据
        Order order = orderFeignClient.queryById(orderId);
        
        // 从Redis中获取用户信息
        User user = JSON.parseObject(redisTemplate.boundHashOps("train_manager:userInfo")
                .get(order.getUserId()).toString(), User.class);
        
        // 创建OrderHandler对象
        OrderHandler orderHandler = new OrderHandler();
        orderHandler.setOrder(order);
        orderHandler.setUser(user);
        
        // 返回
        return orderHandler;
        
    }
}
